package com.example.bankseckill.controller;

import com.example.bankseckill.config.AccessLimit;
import com.example.bankseckill.mapper.SeckillmessageLogMapper;
import com.example.bankseckill.pojo.*;
import com.example.bankseckill.pojo.vo.ProductVo;
import com.example.bankseckill.pojo.vo.RespBean;
import com.example.bankseckill.pojo.vo.RespBeanEnum;
import com.example.bankseckill.rabbitmq.MQsender;
import com.example.bankseckill.service.IOrderService;
import com.example.bankseckill.service.ISeckillOrderService;
import com.example.bankseckill.service.ISeckillProductService;
import com.example.bankseckill.utils.JsonUtil;
import com.example.bankseckill.utils.UUIDUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @Author: liyangjing
 * @Date: 2022/02/09/21:42
 * @Description:
 * 秒杀接口
 */
@RestController
@RequestMapping("/bank/seckill")
@Api(tags = "秒杀模块")
@Slf4j
public class SecKillController implements InitializingBean {

    @Autowired
    ISeckillProductService seckillProductService;

    @Autowired
    private ISeckillOrderService seckillOrderService;

    @Autowired
    private IOrderService orderService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private MQsender mQsender;

    @Autowired
    private SeckillmessageLogMapper seckillmessageLogMapper;

    private static Map<Long,Boolean> EmptyStockMap = new ConcurrentHashMap<>();
    /**
     * 秒杀接口1.2(解决限购、超卖)
     * //优化前QPS 124.0
     * @param user
     * @param SecproductId 秒杀产品id
     * @return
     */
    @AccessLimit(second=5,maxCount=5,needLogin=true)
    @ApiOperation(value = "秒杀商品")
    @RequestMapping(value = "/doSeckill",method = RequestMethod.POST)
    public RespBean getKill(User user,Long SecproductId){
//        log.info("秒杀商品路径+{}",path);
            if(user==null){
                return RespBean.error(RespBeanEnum.KILL_LOGIN_ERROR);
            }
            if(SecproductId==null){
                return RespBean.error("没有找到该商品");
            }
//            判断路径
        ValueOperations valueOperations = redisTemplate.opsForValue();
//        boolean check = orderService.checkSecPath(user,SecproductId,path);
//        if(!check){
//            return RespBean.error("请求非法!请重新尝试!");
//        }
            //       判断是否重复抢购
                SeckillOrder seckillOrder = (SeckillOrder) redisTemplate.opsForValue().get("order:" + user.getId() + ":" +SecproductId);
        if(seckillOrder!=null){
            return RespBean.error(RespBeanEnum.REPEATE_BUY_ERROR);
        }
        //若活动是临时发布的
        if(EmptyStockMap.get(SecproductId)==null){
            EmptyStockMap.put(SecproductId,false);
        }
        //内存标记为ture ，直接返回空库存，减少redis标记
        if(EmptyStockMap.get(SecproductId)){
            return RespBean.error(RespBeanEnum.EMPTY_STOCK);
        }
        //预剪库存
        Long stock = valueOperations.decrement("seckillProducts:" + SecproductId);
        if(stock<0){
            //内存标记为true
            EmptyStockMap.put(SecproductId,true);
            valueOperations.increment("seckillProducts:" + SecproductId);
            return RespBean.error(RespBeanEnum.EMPTY_STOCK);
        }
        //下单操作
        //消息对象
        String seckillmsgId = UUIDUtil.uuid();
        SeckillmessageLog seckillmessageLog = new SeckillmessageLog();
        //消息id
        seckillmessageLog.setMsgId(seckillmsgId);
        //用户id
        seckillmessageLog.setUserId(user.getId());
        //秒杀产品id
        seckillmessageLog.setSecproductId(SecproductId);
        //消息状态（0：消息投递中 1：投递成功 2：投递失败）
        seckillmessageLog.setStatus(0);
        //路由键
        seckillmessageLog.setRouteKey(SecKillMessageConstants.ROUTING_KEY_NAME);
        //交换机
        seckillmessageLog.setExchange(SecKillMessageConstants.EXCHANGE_NAME);
        //重试次数
        seckillmessageLog.setCount(0);
        //重试时间
        seckillmessageLog.setTryTime(LocalDateTime.now().plusMinutes(SecKillMessageConstants.MSG_TIMEOUT));
        //创建时间
        seckillmessageLog.setCreateTime(LocalDateTime.now());
        //更新时间
        seckillmessageLog.setUpdateTime(LocalDateTime.now());
        seckillmessageLogMapper.insert(seckillmessageLog);
        mQsender.sendSeckillMessage(seckillmessageLog,seckillmsgId);
        return  RespBean.success(0);
    }

    /**
     *  定时获取秒杀结果
     * @param user
     * @param seckillProductId
     * @return orderId ：成功  -1秒杀失败， 0排队中
     */
    @ApiOperation(value = "定时获取秒杀结果（orderId ：成功,  -1秒杀失败,0排队中）")
    @RequestMapping(value = "/result",method = RequestMethod.GET)
    public RespBean getSecKillResult(User user,Long seckillProductId){
        boolean b = isRisk(user, seckillProductId);
        if(b) {
            return RespBean.error(RespBeanEnum.Busy_Activities);
        }
        log.info("定时获取秒杀结果+{}",seckillProductId);
        if(user == null){
            return RespBean.error(RespBeanEnum.KILL_LOGIN_ERROR);
        }
        Long orderId = seckillOrderService.getResult(user,seckillProductId);
        return RespBean.success("秒杀成功!",orderId);
    }

    /**
     * 获取秒杀地址
     *
     *
     * @param user
     * @param seckillProductId
     * @return
     */
    @ApiOperation(value = "获取秒杀地址")
    @RequestMapping(value = "/path",method = RequestMethod.GET)
    public RespBean getPath(User user,Long seckillProductId){
//        初步筛选
        boolean b = isRisk(user, seckillProductId);
        //活动火爆 资格不够
        if(b) {
            return RespBean.error(RespBeanEnum.Busy_Activities);
        }
        log.info("获取秒杀地址 秒杀产品id ===》{}",seckillProductId);
        //用户是否登录
        if(user==null){
            return RespBean.error(RespBeanEnum.KILL_LOGIN_ERROR);
        }
        if(seckillProductId==null){
            return RespBean.error("没有此商品！！");
        }
        //判断商品是否 已经到结束时间
        //现在缓存中查找这个产品
        ProductVo  productVo = (ProductVo) redisTemplate.opsForValue().get("SecProductDetail:" + seckillProductId);
        //如果没有查到，就在数据库查
        if(productVo==null){
            productVo = seckillProductService.findProductVoById(seckillProductId);
            redisTemplate.opsForValue().set("SecProductDetail:" + seckillProductId,productVo,30, TimeUnit.MINUTES);
        }
        //如果数据没有
        if(productVo==null){
            return RespBean.error("没有此商品！！");
        }
        //判断活动是否开始
        if(productVo.getStartDate().after(new Date())){
            return RespBean.error("秒杀活动尚未开始");
        }
        //判断活动是否结束
        if(productVo.getEndDate().before(new Date())){
            return RespBean.error("秒杀活动已经结束了");
        }
        //如果活动被禁用
        if(productVo.getEnable()==0){
            return RespBean.error("活动已被禁用,无法购买！");
        }
        String path = orderService.createSecKillPath(user,seckillProductId);
        log.info("获取的秒杀路径{}",path);
        return RespBean.success(path);
    }

    /**
     * 风险用户等级>产品要求等级
     * @param user
     * @param seckillProductId
     * @return  活动繁忙
     */

    public boolean isRisk(User user,Long seckillProductId){
        Integer productRiskLevel = (Integer) redisTemplate.opsForValue().get("RiskLevel:" + seckillProductId);
        if(productRiskLevel==null) return true;
        return user.getRiskLevel() > productRiskLevel;
    }
    /**
     * 初始化时，可以把商品库存数量加载到Redis里面
     *
     * 同时    缓存商品等级
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
       List<ProductVo> list =  seckillProductService.findProductVo();
       if(CollectionUtils.isEmpty(list)){
           return;
       }
       list.forEach(productVo -> {
           redisTemplate.opsForValue().set("seckillProducts:"+productVo.getPid(),productVo.getStockCount());
           //秒杀产品id
           EmptyStockMap.put((long) productVo.getPid(),false);
//           缓存商品等级
           redisTemplate.opsForValue().set("RiskLevel:"+productVo.getPid(),productVo.getRiskLevel());
       });
    }
}